/*
 * Copyright (c) 2002, 2004, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package com.sun.corba.se.impl.transport;

import java.util.Iterator;
import java.util.List;

import org.omg.CORBA.COMM_FAILURE;
import org.omg.CORBA.CompletionStatus;
import org.omg.CORBA.INTERNAL;
import org.omg.CORBA.SystemException;

import com.sun.corba.se.pept.transport.ContactInfo;
import com.sun.corba.se.pept.transport.ContactInfoList;

import com.sun.corba.se.spi.ior.IOR;
import com.sun.corba.se.spi.logging.CORBALogDomains;
import com.sun.corba.se.spi.orb.ORB;
import com.sun.corba.se.spi.transport.CorbaContactInfo;
import com.sun.corba.se.spi.transport.CorbaContactInfoList;
import com.sun.corba.se.spi.transport.CorbaContactInfoListIterator;
import com.sun.corba.se.spi.transport.IIOPPrimaryToContactInfo;

import com.sun.corba.se.impl.logging.ORBUtilSystemException;
import com.sun.corba.se.impl.protocol.CorbaInvocationInfo;

// REVISIT: create a unit test for this class.

public class CorbaContactInfoListIteratorImpl
    implements
    CorbaContactInfoListIterator {

  protected ORB orb;
  protected CorbaContactInfoList contactInfoList;
  protected CorbaContactInfo successContactInfo;
  protected CorbaContactInfo failureContactInfo;
  protected RuntimeException failureException;

  // ITERATOR state
  protected Iterator effectiveTargetIORIterator;
  protected CorbaContactInfo previousContactInfo;
  protected boolean isAddrDispositionRetry;
  protected IIOPPrimaryToContactInfo primaryToContactInfo;
  protected ContactInfo primaryContactInfo;
  protected List listOfContactInfos;
  // End ITERATOR state

  public CorbaContactInfoListIteratorImpl(
      ORB orb,
      CorbaContactInfoList corbaContactInfoList,
      ContactInfo primaryContactInfo,
      List listOfContactInfos) {
    this.orb = orb;
    this.contactInfoList = corbaContactInfoList;
    this.primaryContactInfo = primaryContactInfo;
    if (listOfContactInfos != null) {
      // listOfContactInfos is null when used by the legacy
      // socket factory.  In that case this iterator is NOT used.
      this.effectiveTargetIORIterator = listOfContactInfos.iterator();
    }
    // List is immutable so no need to synchronize access.
    this.listOfContactInfos = listOfContactInfos;

    this.previousContactInfo = null;
    this.isAddrDispositionRetry = false;

    this.successContactInfo = null;
    this.failureContactInfo = null;
    this.failureException = null;

    primaryToContactInfo = orb.getORBData().getIIOPPrimaryToContactInfo();
  }

  ////////////////////////////////////////////////////
  //
  // java.util.Iterator
  //

  public boolean hasNext() {
    // REVISIT: Implement as internal closure iterator which would
    // wraps sticky or default.  Then hasNext and next just call
    // the closure.

    if (isAddrDispositionRetry) {
      return true;
    }

    boolean result;

    if (primaryToContactInfo != null) {
      result = primaryToContactInfo.hasNext(primaryContactInfo,
          previousContactInfo,
          listOfContactInfos);
    } else {
      result = effectiveTargetIORIterator.hasNext();
    }

    return result;
  }

  public Object next() {
    if (isAddrDispositionRetry) {
      isAddrDispositionRetry = false;
      return previousContactInfo;
    }

    // We hold onto the last in case we get an addressing
    // disposition retry.  Then we use it again.

    // We also hold onto it for the sticky manager.

    if (primaryToContactInfo != null) {
      previousContactInfo = (CorbaContactInfo)
          primaryToContactInfo.next(primaryContactInfo,
              previousContactInfo,
              listOfContactInfos);
    } else {
      previousContactInfo = (CorbaContactInfo)
          effectiveTargetIORIterator.next();
    }

    return previousContactInfo;
  }

  public void remove() {
    throw new UnsupportedOperationException();
  }

  ////////////////////////////////////////////////////
  //
  // com.sun.corba.se.pept.transport.ContactInfoListIterator
  //

  public ContactInfoList getContactInfoList() {
    return contactInfoList;
  }

  public void reportSuccess(ContactInfo contactInfo) {
    this.successContactInfo = (CorbaContactInfo) contactInfo;
  }

  public boolean reportException(ContactInfo contactInfo,
      RuntimeException ex) {
    this.failureContactInfo = (CorbaContactInfo) contactInfo;
    this.failureException = ex;
    if (ex instanceof COMM_FAILURE) {
      SystemException se = (SystemException) ex;
      if (se.completed == CompletionStatus.COMPLETED_NO) {
        if (hasNext()) {
          return true;
        }
        if (contactInfoList.getEffectiveTargetIOR() !=
            contactInfoList.getTargetIOR()) {
          // retry from root ior
          updateEffectiveTargetIOR(contactInfoList.getTargetIOR());
          return true;
        }
      }
    }
    return false;
  }

  public RuntimeException getFailureException() {
    if (failureException == null) {
      return
          ORBUtilSystemException.get(orb,
              CORBALogDomains.RPC_TRANSPORT)
              .invalidContactInfoListIteratorFailureException();
    } else {
      return failureException;
    }
  }

  ////////////////////////////////////////////////////
  //
  // spi.CorbaContactInfoListIterator
  //

  public void reportAddrDispositionRetry(CorbaContactInfo contactInfo,
      short disposition) {
    previousContactInfo.setAddressingDisposition(disposition);
    isAddrDispositionRetry = true;
  }

  public void reportRedirect(CorbaContactInfo contactInfo,
      IOR forwardedIOR) {
    updateEffectiveTargetIOR(forwardedIOR);
  }

  ////////////////////////////////////////////////////
  //
  // Implementation.
  //

  //
  // REVISIT:
  //
  // The normal operation for a standard iterator is to throw
  // ConcurrentModificationException whenever the underlying collection
  // changes.  This is implemented by keeping a modification counter (the
  // timestamp may fail because the granularity is too coarse).
  // Essentially what you need to do is whenever the iterator fails this
  // way, go back to ContactInfoList and get a new iterator.
  //
  // Need to update CorbaClientRequestDispatchImpl to catch and use
  // that exception.
  //

  public void updateEffectiveTargetIOR(IOR newIOR) {
    contactInfoList.setEffectiveTargetIOR(newIOR);
    // If we report the exception in _request (i.e., beginRequest
    // we cannot throw RemarshalException to the stub because _request
    // does not declare that exception.
    // To keep the two-level dispatching (first level chooses ContactInfo,
    // second level is specific to that ContactInfo/EPT) we need to
    // ensure that the request dispatchers get their iterator from the
    // InvocationStack (i.e., ThreadLocal). That way if the list iterator
    // needs a complete update it happens right here.
    ((CorbaInvocationInfo) orb.getInvocationInfo())
        .setContactInfoListIterator(contactInfoList.iterator());
  }
}

// End of file.
